/*
 *  Copyright (C) 2009 Antonio Mata Chamorro
 *  
 *  Este programa es software libre: usted puede redistribuirlo y/o modificarlo
 *  bajo los t�rminos de la Licencia P�blica General GNU publicada
 *  por la Fundaci�n para el Software Libre, ya sea la versi�n 3
 *  de la Licencia, o (a su elecci�n) cualquier versi�n posterior.
 *  
 *  Este programa se distribuye con la esperanza de que sea �til, pero 
 *  SIN GARANT�A ALGUNA; ni siquiera la garant�a impl�cita
 *  MERCANTIL o de APTITUD PARA UN PROP�SITO DETERMINADO. 
 *  Consulte los detalles de la Licencia P�blica General GNU para obtener 
 *  una informaci�n m�s detallada. 
 *  
 *  Deber�a haber recibido una copia de la Licencia P�blica General GNU
 *  junto a este programa. 
 *  En caso contrario, consulte <http://www.gnu.org/licenses/>.
 *   
 */
package org.javahispano.javacup.tacticas_aceptadas.amata.com.amata.javacup.tacticas;

import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.Posicion;
import org.newdawn.slick.geom.Circle;
import org.newdawn.slick.geom.Line;

/**
 * Calcula el mejor {@link Kick} desde una posicion a porter�a.
 * 
 * <p/>
 * 
 * Se eval�a si es posible lanzar la pelota hacia el centro de la porter�a, un
 * lado u otro lado seg�n si hay alg�n jugador contrario en el camino o no.
 * 
 * @author Antonio Mata Chamorro
 * */
public class StrikeCalculator {

	private boolean centerIntersects;
	private boolean rightIntersects;
	private boolean leftIntersects;
	private Kick auxKick = new Kick();
	private Spot auxSpot = new Spot(new Posicion(0, 0), 0);
	private final float PLAYER_DIAMETER = 5f;
	private final float KICK_FORCE = 1f;
	private float kickElevation = 20f;
	private float dirtyStrikeFactor = 0.5f;

	public StrikeCalculator(SoccerMatchConfiguration configuration) {
		centerIntersects = false;
		rightIntersects = false;
		leftIntersects = false;

		this.kickElevation = configuration.getKickElevation();
		this.dirtyStrikeFactor = configuration.getDirtyStrikeFactor();

		auxKick.setElevation(kickElevation);
		auxKick.setForce(KICK_FORCE);

	}

	/**
	 * Se comprueba si un tiro al centro de la porter�a, a la izquierda o la
	 * derecha colisiona con alg�n jugador contrario. Se escogera el tiro a
	 * puerta all� donde no hayan contrarios. El valor del tiro eval�a las
	 * posibilidades de marcar con este tiro, depende de la distancia de jugador
	 * a la porter�a contraria. Si esta justo debajo de los postes de la
	 * porter�a contraria vale 1, si esta en su propia porter�a vale 0.
	 * 
	 * @param player
	 *            jugador que disparara
	 * @param spot
	 *            punto desde el que tirara la pelota
	 * @return despu�s de la llamada a este m�todo contendra los par�metros para
	 *         realizar el tiro a puerta.
	 * */
	public void findKick(SoccerPlayer player, Spot spot, Kick result) {

		Line lineCenter = new Line((float) spot.getPosicion().getX(),
				(float) spot.getPosicion().getY(), (float) player.getTeam()
						.getGoal().getCenterPosition().getX(), (float) player
						.getTeam().getGoal().getCenterPosition().getY());
		Line lineRight = new Line((float) spot.getPosicion().getX(),
				(float) spot.getPosicion().getY(), (float) player.getTeam()
						.getGoal().getRightPosicion().getX(), (float) player
						.getTeam().getGoal().getRightPosicion().getY());
		Line lineLeft = new Line((float) spot.getPosicion().getX(),
				(float) spot.getPosicion().getY(), (float) player.getTeam()
						.getGoal().getLeftPosicion().getX(), (float) player
						.getTeam().getGoal().getLeftPosicion().getY());
		centerIntersects = rightIntersects = leftIntersects = false;

		for (SoccerPlayer p : player.getTeam().getOppositeTeam().getPlayers()) {
			Circle circle = new Circle((float) p.getPosicion().getX(),
					(float) p.getPosicion().getY(), PLAYER_DIAMETER);

			centerIntersects = centerIntersects
					|| circle.intersects(lineCenter);
			rightIntersects = rightIntersects || circle.intersects(lineRight);
			leftIntersects = leftIntersects || circle.intersects(lineLeft);
		}

		result.setPosicion(player.getTeam().getGoal().getCenterPosition());
		result.setForce(KICK_FORCE);
		result.setElevation(kickElevation);

		if (centerIntersects) {
			if (!rightIntersects)
				result.setPosicion(player.getTeam().getGoal()
						.getRightPosicion());
			else if (!leftIntersects)
				result
						.setPosicion(player.getTeam().getGoal()
								.getLeftPosicion());
			else {
				// hay un jugador delante de la porter�a, se reduce las
				// posibilidades de conseguir un gol
				result.setValue(result.getValue() * dirtyStrikeFactor);

			}
		}

		// si estamos en el area grande tiramos a donde no este el portero
		if (player.getPosicion().distancia(
				player.getTeam().getGoal().getCenterPosition()) < Constantes.ANCHO_AREA_GRANDE * 2) {

			if (player.getTeam().getOppositeTeam().getGoalKeeper()
					.getPosicion().getX() > Constantes.DISTANCIA_CONTROL_BALON_PORTERO) {
				result
						.setPosicion(player.getTeam().getGoal()
								.getLeftPosicion());
			} else if (player.getTeam().getOppositeTeam().getGoalKeeper()
					.getPosicion().getX() < -Constantes.DISTANCIA_CONTROL_BALON_PORTERO) {
				result.setPosicion(player.getTeam().getGoal()
						.getRightPosicion());
			} else {
				result.setPosicion(player.getTeam().getGoal()
						.getCenterPosition());
			}
		}

		// es 0 si esta en la otra punteria y 1 si esta justo debajo de la
		// porter�a contraria
		result
				.setValue((float) (1 - ((spot.getPosicion()
						.distancia(Constantes.centroArcoSup)) / Constantes.LARGO_CAMPO_JUEGO)));
	}

	/**
	 * Calcula un tiro de un jugador desde su punto actual
	 * 
	 * @see #findKick(SoccerPlayer, Spot, Kick)
	 * */
	public void findKick(SoccerPlayer player, Kick result) {
		auxSpot.setPosicion(player.getPosicion());
		auxSpot.setValue(0);

		findKick(player, auxSpot, result);

		// si estamos mas alla del area grande dividimos las posibilidades a la
		// mitad.
		if (player.getPosicion().distancia(Constantes.centroArcoSup) > Constantes.LARGO_AREA_GRANDE)
			result.setValue(result.getValue() / 2);
	}

	/**
	 * Este m�todo calcula las posibilidades de marcar desde el punto y lo carga
	 * en el punto con el m�todo {@link Spot#setValue(float)}. La estimaci�n se
	 * hace tal como en {@link #findKick(SoccerPlayer, Spot, Kick)}
	 * 
	 * */
	public void findKick(SoccerPlayer player, Spot spot) {
		// es 0 si esta en la otra punteria y 1 si esta justo debajo de la
		// porter�a contraria
		spot
				.setValue((float) (1 - ((spot.getPosicion()
						.distancia(Constantes.centroArcoSup)) / Constantes.LARGO_CAMPO_JUEGO)));

		// si estamos mas alla del area grande dividimos las posibilidades a la
		// mitad.
		if (player.getPosicion().distancia(Constantes.centroArcoSup) > Constantes.LARGO_AREA_GRANDE)
			spot.setValue(spot.getValue() / 2);

	}
}